﻿using System.Collections.Generic;
using Deferred.Library.Scene.Graphics;
using Deferred.Library.Scene.Visual;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Deferred.Library.Deferred
{
    class SceneRenderEngine : GraphicalComponent
    {
        private DualContentManager content;
        private Effect drawObjectEffect;
        private Effect terrainEffect;

        private EffectParameter lowTextureParameter;
        private EffectParameter lowNormalParameter;
        private EffectParameter midTextureParameter;
        private EffectParameter midNormalParameter;
        private EffectParameter highTextureParameter;
        private EffectParameter highNormalParameter;
        private EffectParameter slopeTextureParameter;
        private EffectParameter slopeNormalParameter;
        private EffectParameter terrainWorldParameter;

        private EffectParameter objectWorldParameter;
        private EffectParameter objectTextureParameter;
        private EffectParameter specularMapParameter;
        private EffectParameter normalMapParameter;

        public SceneRenderEngine(DeferredBase deferredBase)
            : base(deferredBase.DeviceService)
        {
            this.content = deferredBase.Content;
        }

        public void Initialize()
        {
            drawObjectEffect = content.InternalContent.Load<Effect>("DrawObject");
            terrainEffect = content.InternalContent.Load<Effect>("Terrain");

            lowTextureParameter = terrainEffect.Parameters["lowTexture"];
            lowNormalParameter = terrainEffect.Parameters["lowNormal"];
            midTextureParameter = terrainEffect.Parameters["midTexture"];
            midNormalParameter = terrainEffect.Parameters["midNormal"];
            highTextureParameter = terrainEffect.Parameters["highTexture"];
            highNormalParameter = terrainEffect.Parameters["highNormal"];
            slopeTextureParameter = terrainEffect.Parameters["slopeTexture"];
            slopeNormalParameter = terrainEffect.Parameters["slopeNormal"];
            terrainWorldParameter = terrainEffect.Parameters["World"];

            objectWorldParameter = drawObjectEffect.Parameters["World"];
            objectTextureParameter = drawObjectEffect.Parameters["Texture"];
            specularMapParameter = drawObjectEffect.Parameters["SpecularMap"];
            normalMapParameter = drawObjectEffect.Parameters["NormalMap"];
        }

        public void DrawObjects(IList<TerrainVisual> terrains, IList<DrawableObjectVisual> objects)
        {
            GraphicsDevice.RenderState.DepthBufferEnable = true;
            GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            GraphicsDevice.RenderState.AlphaBlendEnable = false;

            foreach (DrawableObjectVisual o in objects)
            {
                DrawObject(o);
            }
            foreach (TerrainVisual t in terrains)
            {
                DrawTerrain(t);
            }
        }

        private void DrawTerrain(TerrainVisual t)
        {
            lowTextureParameter.SetValue(t.LowTexture);
            lowNormalParameter.SetValue(t.LowNormal);
            midTextureParameter.SetValue(t.MidTexture);
            midNormalParameter.SetValue(t.MidNormal);
            highTextureParameter.SetValue(t.HighTexture);
            highNormalParameter.SetValue(t.HighNormal);
            slopeTextureParameter.SetValue(t.SlopeTexture);
            slopeNormalParameter.SetValue(t.SlopeNormal);
            terrainWorldParameter.SetValue(Matrix.CreateTranslation(t.Terrain.Position));
            GraphicsDevice.VertexDeclaration = t.VertexDeclaration;
            GraphicsDevice.Indices = t.IndexBuffer;
            GraphicsDevice.Vertices[0].SetSource(t.VertexBuffer, 0, TerrainVertex.SizeInBytes);
            terrainEffect.Begin();
            foreach (EffectTechnique technique in terrainEffect.Techniques)
            {
                foreach (EffectPass pass in technique.Passes)
                {
                    pass.Begin();
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, t.VertexCount, 0, t.TriangleCount);
                    pass.End();
                }
            }
            terrainEffect.End();
        }

        private void DrawObject(DrawableObjectVisual o)
        {
            objectWorldParameter.SetValue(o.World);
            foreach (DeferredModelMesh mesh in o.Model.Meshes)
            {
                foreach (DeferredModelMeshPart part in mesh.MeshParts)
                {
                    objectTextureParameter.SetValue(part.Texture);
                    specularMapParameter.SetValue(part.SpecularMap);
                    normalMapParameter.SetValue(part.NormalMap);
                    GraphicsDevice.VertexDeclaration = part.VertexDeclaration;
                    GraphicsDevice.Vertices[0].SetSource(part.VertexBuffer, 0, part.VertexStride);
                    GraphicsDevice.Indices = part.IndexBuffer;
                    drawObjectEffect.Begin();
                    foreach (EffectPass pass in drawObjectEffect.CurrentTechnique.Passes)
                    {
                        pass.Begin();
                        GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.VertexCount, 0, part.TriangleCount);
                        pass.End();
                    }
                    drawObjectEffect.End();
                }
            }
        }
    }
}
